package collection;

import exception.CollectionEmptyException;

public abstract class ATree<N extends ATreeNode<E>, E> implements ITree<N, E> {
	
	protected ATree(){}
	
	@Override
	public abstract int size();

	@Override
	public boolean isEmpty() 
	{
		return size() == 0;
	}

	@Override
	public abstract N getRoot() throws CollectionEmptyException;
	
	@Override
	public abstract N createRoot(E element);
	
	@Override
	public abstract N createNode(E element);

	@Override
	public abstract boolean isInternal(N someNode);

	@Override
	public abstract boolean isExternal(N someNode);

	@Override
	public abstract boolean isRoot(N someNode);

	@Override
	public abstract N getParent(N someNode);

	@Override
	public abstract Iterable<N> getChildren(N someNode);

	@Override
	public E getElement(N someNode) 
	{
		return someNode.getElement();
	}

	@Override
	public void setElement(N someNode, E element) 
	{
		someNode.setElement(element);
	}
	
	public int depth(N someNode)
	{
		if (isRoot(someNode))
			return 0;
		else 
			return depth(getParent(someNode)) + 1;
	}
	
	public int height(N someNode)
	{
		if (isExternal(someNode))
			return 0;
		else
		{
			int maxHeight = 0;
			for (N child : getChildren(someNode))
			{
				maxHeight = Math.max(maxHeight, height(child));
			}
			return maxHeight;
		}
	}

	public void preOrderTraverse(N someNode, ANodeCommand<E> command)
	{
		command.receiveNode(someNode).execute();
		if (!isExternal(someNode))
		{
			for (N child : getChildren(someNode))
			{
				preOrderTraverse(child, command);
			}
		}
	}
	
	public void postOrderTraverse(N someNode, ANodeCommand<E> command)
	{
		if (!isExternal(someNode))
		{
			for (N child : getChildren(someNode))
			{
				postOrderTraverse(child, command);
			}
		}
		command.receiveNode(someNode).execute();
	}
	
	private String toStringOfSubTree(N someNode)
	{
		String retStr = someNode.getElement().toString();
		if (isInternal(someNode))
		{
			retStr += "(";
			boolean isFirstChild = true;
			for (N childNode : getChildren(someNode))
			{
				if (isFirstChild)
				{
					retStr += toStringOfSubTree(childNode);
					isFirstChild = false;
				}
				else
				{
					retStr += "," + toStringOfSubTree(childNode);
				}
			}
			retStr += ")";
		}
		
		return retStr;
	}
	
	@Override
	public String toString()
	{
		return toStringOfSubTree(getRoot());
	}
}
